home *** CD-ROM | disk | FTP | other *** search
/ Aminet 5 / Aminet 5 - March 1995.iso / Aminet / mus / edit / AlgoRhythms.lha / AlgoRhythms / Source / musicserial.c < prev    next >
C/C++ Source or Header  |  1994-12-09  |  16KB  |  612 lines

  1. /* MusicSerial.c
  2.     Copyright (c) 1990,1991,1992,1993 by Thomas E. Janzen
  3.     All Rights Reserved
  4.  
  5.     THIS SOFTWARE IS FURNISHED FREE OF CHARGE FOR STUDY AND USE AND MAY
  6.     BE COPIED ONLY FOR PERSONAL USE OR COMPLETELY AS OFFERED WITH NO
  7.     CHANGES FOR FREE DISTRIBUTION.  NO TITLE TO AND OWNERSHIP OF THE
  8.     SOFTWARE IS HEREBY TRANSFERRED.  THOMAS E. JANZEN ASSUMES NO 
  9.     RESPONSIBILITY FOR THE USE OR RELIABILITY OF THIS SOFTWARE.
  10.     
  11.     Thomas E. Janzen
  12.     208A Olde Derby Road
  13.     Norwood, MA  02062-1761
  14.     (617)769-7733
  15. **  FACILITY:
  16. **
  17. **    AlgoRhythms music improviser on Commodore (TM) Amiga (TM)
  18. **    compiled with SAS/C Amiga Compiler 6.50 
  19. **
  20. **  ABSTRACT:
  21. **
  22. **    MusicSerial.c manages the serial device at the MIDI bit rate.
  23. **    All sends to MIDI occur here.
  24. **
  25. **  AUTHORS: Thomas E. Janzen
  26. **
  27. **  CREATION DATE:    26-MAR-1990
  28. **
  29. **  MODIFICATION HISTORY:
  30. **    DATE    NAME    DESCRIPTION
  31. **  7 Dec 90 T. Janzen Used SendIO rather than DoIO - didn't up performance
  32. **  4 Nov 91 T. Janzen incorporate MIDI running status; delete SendNoteOff
  33. **  8 DEC 91 T. Janzen conform to SAS/C 5.10b remove extern from functs
  34. **  4 Jan 92 TEJ  last changes for 2.0
  35. **  14 SEP 92 TEJ Make play_note_on use NOTEOFF when dynamic is zero 
  36. **                  (works, fixes stuck note bug)
  37. **  15 SEP 92 TEJ Fix stuck notes by using MAXVOICE in stop_all_notes
  38. *    2 AUG 93 TEJ stop guru's by checking before set audio call
  39. **   6 DEC 94 TEJ remove SERIALNAME from CreatePort.  Change flags in
  40. **                OpenDevice serial ExtIO (esp. remove 7 wire).
  41. **  10 DEC 94 TEJ Improved error reporting with error number.
  42. **                Made it work with audio even though serial device fails.
  43. **--
  44. */
  45.  
  46. #include <exec/types.h>
  47. #include <exec/nodes.h>
  48. #include <exec/lists.h>
  49. #include <exec/ports.h>
  50. #include <exec/libraries.h>
  51. #include <exec/devices.h>
  52. #include <devices/serial.h>
  53. #include <exec/io.h>
  54. #include <intuition/intuition.h>
  55. #include <proto/dos.h>
  56. #include <proto/graphics.h>
  57. #include <proto/exec.h>
  58. #include <proto/mathffp.h>
  59. #include <proto/intuition.h>
  60. #ifdef CLI
  61. #include <stdio.h>
  62. #endif
  63. #include "Window.h"
  64. #include "AlgoRhythms.h"
  65. #include "MusicSerial.h"
  66. #include "Record.h"
  67. #include "audio.h"
  68.  
  69. #define NOTEONCMD    (0x90)    /* MIDI Note on byte         */
  70. #define NOTEOFFCMD   (0x80)    /* MIDI Note off byte        */
  71. #define START        (0xFA)    /* MIDI Start play byte     */
  72. #define STOP         (0xFC)    /* MIDI Stop play byte        */
  73. #define CONTINUE     (0xFB)    /* MIDI Continue play byte    */
  74. #define TIMINGCLOCK  (0xF8)    /* MIDI Timing clock byte    */
  75.  
  76. static int response;
  77.  
  78. static struct IOExtSer *ior_ser = NULL;
  79. static struct MsgPort *port = NULL;
  80.  
  81. static int serial_fubar = FALSE;
  82.  
  83. extern struct IORequest *CreateExtIO();
  84. extern void DeletePort(struct MsgPort *);
  85.  
  86. /* strings for error alerts */
  87. static char quit_str[5]      = "Quit",
  88.             ser_err_str[20]  = "Serial Device Error",
  89.             prt_err_str[11]  = "Port Error",
  90.             io_err_str[19]   = "Create ExtIO Error",
  91.             dev_err_str[20]  = "Serial Device Error",
  92.             prm_err_str[15]  = "Set Parm Error",
  93.             wrt_err_str[26]  = "Serial.device write error";
  94. #ifndef CLI
  95. struct IntuiText quit_txt 
  96.     = {2, 1, JAM1, 5, 4, &font_choice, quit_str, NULL};
  97. #endif
  98. static char    playbuffer[4];    /* MIDI note message buffer     */
  99.  
  100. #ifdef MEASURE
  101. unsigned int gi_notes_measure = 0;
  102. #endif
  103.  
  104. static char DBGstr[80];
  105. #ifndef CLI
  106. static struct IntuiText 
  107.     DBGTxt = {2, 1, JAM1, 5, 15, &font_choice, DBGstr, NULL};
  108. #endif
  109.  
  110. static void AutoError(const int ErrorNum, const char *ErrorStr);
  111.  
  112. static int set_serial(struct IOExtSer *io, unsigned long rbuf_len,
  113.     unsigned char rlen, unsigned char wlen, unsigned long brk, 
  114.     unsigned long baud, unsigned char sf, 
  115.     unsigned long ta0, unsigned long ta1);
  116.  
  117. static int set_serial(struct IOExtSer *io, unsigned long rbuf_len,
  118.     unsigned char rlen, unsigned char wlen, unsigned long brk, 
  119.     unsigned long baud, unsigned char sf, 
  120.     unsigned long ta0, unsigned long ta1)
  121. /*
  122. ** FUNCTIONAL DESCRIPTION:
  123. **  Sets up serial port
  124. **
  125. ** RETURN VALUE:
  126. **      description: 
  127. **        data_type: 
  128. **
  129. ** ARGUMENTS:
  130. **
  131. **  io-
  132. **         description: IO device structure
  133. **           data_type: pointer to struct IOExtSer
  134. **              access: read/write
  135. **
  136. **  rbuf_len-
  137. **         description: read buffer length returned
  138. **           data_type: unsigned long
  139. **              access: read only
  140. **
  141. **  rlen-
  142. **         description: length of read buffer
  143. **           data_type: unsigned char
  144. **              access: read only
  145. **
  146. **  wlen-
  147. **         description: length of write buffer
  148. **           data_type: unsigned char
  149. **              access: read only
  150. **
  151. **  brk-
  152. **         description: length of break time
  153. **           data_type: unsigned long
  154. **              access: read only
  155. **
  156. **  baud-
  157. **         description: baud rate
  158. **           data_type: unsigned long
  159. **              access: read only
  160. **
  161. **  sf-
  162. **         description: serial flags
  163. **           data_type: unsigned char
  164. **              access: read only
  165. **
  166. **  ta0-
  167. **         description: misc flags
  168. **           data_type: unsigned long
  169. **              access: read only
  170. **
  171. **  ta1-
  172. **         description: misc flags
  173. **           data_type: unsigned long
  174. **              access: read only
  175. **
  176. ** DESIGN:
  177. **  ROUTINE
  178. **  : Fill out io structure for setting up serial device
  179. **  : send the command to the serial device
  180. **  : IF set up failed
  181. **  : : set quit and fubar
  182. **  : : post error
  183. **  : ENDIF
  184. **  : return error
  185. **  ENDROUTINE
  186. */
  187. {
  188.     auto int error;
  189.     
  190.     io->io_ReadLen    = rlen;
  191.     io->io_BrkTime    = brk; /*length of break time (irrelevant)*/
  192.     io->io_Baud       = baud;
  193.     io->io_WriteLen   = wlen;
  194.     io->io_StopBits   = 0x01;
  195.     io->io_RBufLen    = rbuf_len;
  196.     io->io_SerFlags 
  197.       |= SERF_XDISABLED | SERF_SHARED | SERF_RAD_BOOGIE;
  198.     io->IOSer.io_Command = SDCMD_SETPARAMS;
  199.     io->io_TermArray.TermArray0 = ta0;
  200.     io->io_TermArray.TermArray1 = ta1;
  201.     if (!serial_fubar) 
  202.     {
  203.         if ((error = DoIO(io)) != 0) 
  204.         {
  205.             serial_fubar = TRUE;
  206.             AutoError(io->IOSer.io_Error, ser_err_str);
  207.         }
  208.     }
  209.     return error;
  210. }
  211.  
  212. void open_midi_port(void)
  213. /*
  214. ** FUNCTIONAL DESCRIPTION:
  215. **  Opens the serial devices for MIDI usage
  216. **
  217. ** DESIGN:
  218. **  ROUTINE
  219. **  : CreatePort to serial device
  220. **  : IF failed
  221. **  : : set quit and fubar
  222. **  : : post error
  223. **  : ENDIF
  224. **  : ior_ser = CreateExtIO()
  225. **  : IF failed
  226. **  : : set quit and fubar
  227. **  : : post error
  228. **  : ENDIF
  229. **  : OpenDevice()
  230. **  : IF failed
  231. **  : : set quit and fubar
  232. **  : : post error
  233. **  : ENDIF
  234. **  : IF not failed yet
  235. **  : : set_serial()
  236. **  : : IF failed
  237. **  : : : set fubar and quit
  238. **  : : : post error
  239. **  : : ENDIF
  240. **  : ENDIF
  241. **  : return
  242. **  ENDROUTINE
  243. */
  244. {
  245.     auto int error;
  246.     auto unsigned long  rbl = 512,    /*read buffer length*/
  247.                         brk = 750000, /* length of break in usec */
  248.                         baud = 31250, /* MIDI baud rate 31.25k bits/sec*/
  249.                         t0  = 0x51040303,    /*termination characters*/
  250.                         t1  = 0x03030303;
  251.     auto unsigned char  rwl = 0x08, /*bits per read char */
  252.                         wwl = 0x08, /*bits per write char */
  253.                         sf  = 0x00; /*serial flags */
  254.  
  255.     port = CreatePort(0, 0);
  256.     if (NULL == port)
  257.     {
  258.  
  259.         serial_fubar = TRUE;
  260.         AutoError(0, prt_err_str);
  261.         stop_midi();
  262.     }
  263.     ior_ser
  264.         = (struct IOExtSer *)CreateExtIO(port, sizeof(struct IOExtSer));
  265.     if (NULL == ior_ser) 
  266.     {
  267.         serial_fubar = TRUE;
  268.         AutoError(ior_ser->IOSer.io_Error, io_err_str);
  269.         stop_midi();
  270.     }
  271.     ior_ser->io_SerFlags 
  272.       = SERF_XDISABLED | SERF_SHARED | SERF_RAD_BOOGIE;
  273.     if ((error = OpenDevice(SERIALNAME, 0,
  274.         (struct IORequest *)ior_ser, 0)) != 0) 
  275.     {
  276.         serial_fubar = TRUE;
  277.  
  278.         AutoError(ior_ser->IOSer.io_Error, dev_err_str);
  279.         stop_midi();
  280.     }
  281.  
  282.     if (!gi_quit && !gi_fubar && !serial_fubar)
  283.     {
  284.         if ((error 
  285.         = set_serial(ior_ser, rbl, rwl, wwl, brk, baud, sf, t0, t1)) != 0)
  286.         {
  287.             serial_fubar = TRUE;
  288.  
  289.             AutoError(ior_ser->IOSer.io_Error, prm_err_str);
  290.  
  291.             stop_midi();
  292.         }
  293.     }
  294.     return;
  295.     /* The serial device is open,  so go ahead and play music.*/
  296. }
  297.  
  298. int write_ser(struct IOExtSer *ser_io, char *data, int length)
  299. /*
  300. ** FUNCTIONAL DESCRIPTION:
  301. **  Write data to serial port.
  302. **
  303. ** RETURN VALUE:
  304. **      description: TRUE if failed, FALSE if succeeded
  305. **        data_type: int
  306. **
  307. ** ARGUMENTS:
  308. **
  309. **  ser_io-
  310. **         description: serial device structure
  311. **           data_type: pointer to IOExtSer
  312. **              access: read only
  313. **
  314. **  data-
  315. **         description: data to send to serial device
  316. **           data_type: pointer to char
  317. **              access: read only
  318. **
  319. **  length-
  320. **         description: length in bytes of serial data
  321. **           data_type: int
  322. **              access: read only
  323. **
  324. ** DESIGN:
  325. **  ROUTINE
  326. **  : WaitIO() in case some send is not done yet
  327. **  : copy send data and length into ser_io
  328. **  : DoIO(ser_io) (send data)
  329. **  : IF failed
  330. **  : : set quit and fubar
  331. **  : : post error
  332. **  : ENDIF
  333. **  : return error
  334. **  ENDROUTINE
  335. */
  336. {
  337.     auto int error;
  338.     WaitIO(ser_io);
  339.     ser_io->IOSer.io_Data = (APTR)data;
  340.     ser_io->IOSer.io_Length = length;
  341.     ser_io->IOSer.io_Command = CMD_WRITE;
  342.     error = DoIO(ser_io);
  343.     if (error)
  344.     {
  345.         serial_fubar = TRUE;
  346.         AutoError(ser_io->IOSer.io_Error, wrt_err_str);
  347.         stop_midi();
  348.     }
  349.     return error;
  350. }
  351.  
  352. void play_note_on(NOTE_EVENT_TYPE *play_event) 
  353. /*
  354. ** FUNCTIONAL DESCRIPTION:
  355. **  Sends note on and note off MIDI commands; tracks running status
  356. **
  357. ** ARGUMENTS:
  358. **
  359. **  play_event-
  360. **         description: Information about a voice and its status
  361. **           data_type: pointer to NOTE_EVENT_TYPE
  362. **              access: read only
  363. **
  364. ** DESIGN:
  365. **  ROUTINE
  366. **  : IF channel is negative
  367. **  : : clear running status
  368. **  : : return
  369. **  : ENDIF
  370. **  : copy channel and NOTEONCMD to playbuffer[0]
  371. **  : copy dynamic to playbuffer[2]
  372. **  : IF this is a note-on
  373. **  : : playbuffer[1] = new pitch from scale
  374. **  : ELSE
  375. **  : : playbuffer[1] = old pitch
  376. **  : ENDIF
  377. **  : IF playbuffer[0] == Running_Status
  378. **  : : write_ser &playbuffer[1], length of 2
  379. **  : ELSE running status has changed
  380. **  : : write_ser &playbuffer[0], length of 3
  381. **  : : set new running status
  382. **  : ENDIF
  383. **  ENDROUTINE
  384. */
  385. {
  386.         static unsigned char Running_Status = 0X00;
  387.         
  388.         if ((play_event->nv_i_channel) < 0)
  389.         {
  390.             Running_Status = 0;
  391.             return;
  392.         }
  393.         playbuffer[0] = (unsigned char)
  394.             (((play_event->nv_i_channel) & 0XF) | NOTEONCMD);
  395.         playbuffer[2] = (unsigned char)(play_event->nv_i_dynamic);
  396.  
  397.         playbuffer[1] = (unsigned char)(play_event->nv_i_cur_pitch);
  398. #ifdef MEASURE
  399.         if (play_event->nv_i_dynamic != 0)
  400.         {
  401.             gi_notes_measure++;
  402.         }
  403. #endif
  404.         if (!serial_fubar) {
  405.             if (playbuffer[0] == Running_Status)
  406.             {
  407.               write_ser(ior_ser, &playbuffer[1], 2); /* send it out MIDI */
  408.             }
  409.             else
  410.             {
  411.               write_ser(ior_ser, playbuffer, 3);    /* send it out MIDI */
  412.               Running_Status = playbuffer[0];
  413.             }
  414.         }
  415.         play_event->nv_i_was_audio = 0;
  416.         return;
  417. }
  418.  
  419. void send_function(const int Function)
  420. /*
  421. ** FUNCTIONAL DESCRIPTION:
  422. **  Sends special MIDI functions down serial line
  423. **
  424. ** ARGUMENTS:
  425. **
  426. **  Function-
  427. **         description: the MIDI function to send out MIDI
  428. **           data_type: int
  429. **              access: read only
  430. **
  431. ** DESIGN:
  432. **  ROUTINE
  433. **  : clear playbuffer[1]
  434. **  : CASE Function
  435. **  : : STARTFUNCT
  436. **  : : : set playbuffer[0] to START
  437. **  : : STOPFUNCT
  438. **  : : : set playbuffer[0] to STOP
  439. **  : : CLOCKFUNCT
  440. **  : : : set playbuffer[0] to TIMINGCLOCK
  441. **  : : CONTFUNCT
  442. **  : : : set playbuffer[0] to CONTINUE
  443. **  : ENDCASE
  444. **  : write_ser playbuffer[0] length of 1
  445. **  ENDROUTINE
  446. */
  447.     playbuffer[1] = 0;
  448.     switch (Function) 
  449.     {
  450.         case STARTFUNCT:
  451.             playbuffer[0] = (unsigned char)START;
  452.             break;
  453.         case STOPFUNCT:
  454.             playbuffer[0] = (unsigned char)STOP;
  455.             break;
  456.         case CLOCKFUNCT:
  457.             playbuffer[0] = (unsigned char)TIMINGCLOCK;
  458.             break;
  459.         case CONTFUNCT:
  460.             playbuffer[0] = (unsigned char)CONTINUE;
  461.             break;
  462.         default:
  463.             break;
  464.     }
  465.     if (!serial_fubar) {
  466.         write_ser(ior_ser, playbuffer, 1);    /* send it out MIDI */
  467.     }
  468.     return;
  469. }
  470.  
  471. void stop_all_notes(NOTE_EVENT_TYPE *notes_to_stop)
  472. /*
  473. ** FUNCTIONAL DESCRIPTION:
  474. **  Sends note-offs for all MIDI notes in all voices
  475. **
  476. ** ARGUMENTS:
  477. **
  478. **  notes_to_stop-
  479. **         description: 
  480. **           data_type: pointer to NOTE_EVENT_TYPE
  481. **              access: read/write only
  482. **
  483. ** DESIGN:
  484. **  ROUTINE
  485. **  : FOR vox_index = 0 to MAXVOICE
  486. **  : : IF notes_to_stop[vox_index] is playing
  487. **  : : : send a note off for this voice
  488. **  : : : IF recording
  489. **  : : : : record a note off for this voice
  490. **  : : : ENDIF
  491. **  : : : Clear playing bit for this voice
  492. **  : : : IF fubar
  493. **  : : : : break out of FOR
  494. **  : : : ENDIF
  495. **  : : ENDIF
  496. **  : ENDFOR
  497. **  : send_function(STOPFUNCT)
  498. **  ENDROUTINE
  499. */
  500. {
  501.     auto int vox_index;
  502.  
  503.     for (vox_index = 0; vox_index < MAXVOICE; vox_index++)
  504.     {
  505.         if (notes_to_stop[vox_index].nv_i_playing)
  506.         {
  507.             Delay(1);
  508.             notes_to_stop[vox_index].nv_i_dynamic = 0;
  509.             if (notes_to_stop[vox_index].nv_i_was_audio) 
  510.             {
  511.                 play_audio_note(&(notes_to_stop[vox_index]));
  512.             }
  513.             else
  514.             {
  515.                 play_note_on(&(notes_to_stop[vox_index]));
  516.             }
  517. #ifndef CLI
  518.             if (recording)
  519.             {
  520.                 record_note_event(&(notes_to_stop[vox_index]));
  521.             }
  522. #endif
  523.             notes_to_stop[vox_index].nv_i_playing = FALSE;
  524.             if (gi_fubar) 
  525.             {
  526.                 break;
  527.             }
  528.         }
  529.     }
  530.     send_function(STOPFUNCT);
  531.     return;
  532. }
  533.  
  534. void stop_midi(void)
  535. /*
  536. ** FUNCTIONAL DESCRIPTION:
  537. **  Disconnects from serial device
  538. **
  539. ** DESIGN:
  540. **  ROUTINE
  541. **  : IF port
  542. **  : : DeletePort
  543. **  : ENDIF
  544. **  : IF ior_ser
  545. **  : : CloseDevice
  546. **  : ENDIF
  547. **  ENDROUTINE
  548. */
  549. {
  550.     if (ior_ser != NULL) 
  551.     {
  552.         CloseDevice(ior_ser);
  553.         DeleteExtIO(ior_ser);
  554.         ior_ser = NULL;
  555.     }
  556.     if (port != NULL) 
  557.     {
  558.         DeletePort(port);
  559.         port = NULL;
  560.     }
  561.     serial_fubar = TRUE;
  562.     return;
  563. }
  564.  
  565. static void AutoError(const int ErrorNum, const char *ErrorStr) {
  566.     auto char NumStr[20];
  567.     switch (ErrorNum) {
  568.       case SerErr_DevBusy:
  569.         sprintf(NumStr, "DevBusy");
  570.         break;
  571.       case SerErr_BaudMismatch:
  572.         sprintf(NumStr, "BaudMismatch");
  573.         break;
  574.       case SerErr_BufErr:
  575.         sprintf(NumStr, "BufErr");
  576.         break;
  577.       case SerErr_InvParam:
  578.         sprintf(NumStr, "InvParam");
  579.         break;
  580.       case SerErr_LineErr:
  581.         sprintf(NumStr, "LineErr");
  582.         break;
  583.       case SerErr_ParityErr:
  584.         sprintf(NumStr, "Parity");
  585.         break;
  586.       case SerErr_TimerErr:
  587.         sprintf(NumStr, "Timer");
  588.         break;
  589.       case SerErr_BufOverflow:
  590.         sprintf(NumStr, "Overflow");
  591.         break;
  592.       case SerErr_NoDSR:
  593.         sprintf(NumStr, "NoDataSetRdy");
  594.         break;
  595.       case SerErr_DetectedBreak:
  596.         sprintf(NumStr, "break came");
  597.         break;
  598.       default:
  599.         sprintf(NumStr, "%ld", ErrorNum);
  600.         break;
  601.     }
  602.     sprintf(DBGstr, "%s %s", ErrorStr, NumStr);
  603. #ifndef CLI
  604.     AutoRequest(w, &DBGTxt, &quit_txt, &quit_txt, 0L, 0L, 300L, 60L);
  605. #else
  606.     puts(DBGstr);
  607. #endif
  608.  
  609.     return;
  610. }
  611.